/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.workflow.service.upcoming;

import com.google.common.base.Strings;
import com.je.bpm.core.model.BpmnModel;
import com.je.bpm.core.model.FlowElement;
import com.je.bpm.core.model.config.ProcessRemindTypeEnum;
import com.je.bpm.core.model.config.process.ProcessRemindTemplate;
import com.je.bpm.core.model.task.KaiteBaseUserTask;
import com.je.bpm.engine.RepositoryService;
import com.je.bpm.engine.impl.cmd.SubmitTypeEnum;
import com.je.bpm.engine.impl.identity.Authentication;
import com.je.bpm.engine.impl.persistence.entity.UrgeLogEntity;
import com.je.bpm.engine.upcoming.ActivitiUpcomingRun;
import com.je.bpm.engine.upcoming.UpcomingCommentInfoDTO;
import com.je.bpm.engine.upcoming.UpcomingDTO;
import com.je.common.base.DynaBean;
import com.je.common.base.service.CommonService;
import com.je.common.base.service.MetaService;
import com.je.common.base.spring.SpringContextHolder;
import com.je.common.base.util.DateUtils;
import com.je.common.base.util.SecurityUserHolder;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.workflow.service.push.CommonSystemVariable;
import com.je.workflow.service.push.PushService;
import com.je.workflow.service.push.WorkFlowVariable;
import com.je.workflow.service.push.pojo.MessageDTO;
import com.je.workflow.service.upcoming.complete.impl.AbstractCompleteUpcomingService;
import com.je.workflow.service.user.WorkFlowUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class ActivitiUpcomingRunImpl implements ActivitiUpcomingRun {

    @Autowired
    private MetaService metaService;
    @Autowired
    private CommonService commonService;
    @Autowired
    private AbstractCompleteUpcomingService abstractCompleteUpcomingService;
    @Autowired
    private WorkFlowUserService workFlowUserService;
    @Autowired
    private PushService pushService;

    private static final String DESC_FORMAT = "【%s】催办您尽快办理【%s】任务！催办信息：%s";

    private static final String DESC_COPY_FORMAT = "【%s】催办【%s】的任务【%s】，抄送给您，请知悉！抄送信息：%s";


    private static final String[] NOT_PUSH_MESSAGE_TYPES = new String[]{SubmitTypeEnum.OUTGOING.toString(), SubmitTypeEnum.CANCEL.toString(),
            SubmitTypeEnum.RECEIVE.toString(), SubmitTypeEnum.INVALID.toString(),
            SubmitTypeEnum.RETRIEVE.toString(), SubmitTypeEnum.CANCELDELEGATE.toString()};

    @Override
    public void addUpcoming(UpcomingDTO upcomingDTO) {
        String modelName = upcomingDTO.getModelName();
        String processKey = upcomingDTO.getModelKey();
        ProcessRemindTemplate processRemindTemplate = upcomingDTO.getProcessRemindTemplate();
        UpcomingCommentInfoDTO upcomingInfo = upcomingDTO.getUpcomingInfo();
        String funcCode = upcomingDTO.getFuncCode();
        String tableCode = upcomingDTO.getTableCode();
        String upcomingUserId = upcomingDTO.getUpcomingUserId();
        String taskId = upcomingDTO.getTaskId();
        if (upcomingInfo == null) {
            return;
        }
        if (upcomingInfo.getBeanId() == null) {
            return;
        }
        String beanId = upcomingInfo.getBeanId();
        String comment = upcomingInfo.getComment();
        SubmitTypeEnum submitType = upcomingInfo.getSubmitType();
        String executionId = upcomingDTO.getExecutionId();
        DynaBean execution = metaService.selectOne("JE_WORKFLOW_RN_EXECUTION",
                ConditionsWrapper.builder().eq("JE_WORKFLOW_RN_EXECUTION_ID", executionId));
        if (execution == null) {
            Map<String, Object> bean = upcomingInfo.getBean();
            execution = insertExecution(beanId, executionId, bean, processRemindTemplate, funcCode, tableCode, modelName,
                    submitType, comment, processKey, upcomingDTO);
        } else {
            execution.setStr("EXECUTION_SUBMIT_TYPE", submitType.toString());
            execution.setStr("EXECUTION_LAST_COMMENT", comment);
            execution.setStr("EXECUTION_SUBMIT_USER_NAME", Authentication.getAuthenticatedUser().getName());
            execution.setStr("EXECUTION_SUBMIET_USER_ID", Authentication.getAuthenticatedUser().getDeptId());
            execution.setStr("EXECUTION_NODE_NAME", upcomingDTO.getNodeName());
            execution.setStr("EXECUTION_NODE_ID", upcomingDTO.getNodeId());
            execution.setStr("EXECUTION_CONTENT", buildContent(processRemindTemplate, modelName, submitType, upcomingDTO));
            if (upcomingDTO.getUpcomingInfo().getAssigneeJson() != null && !upcomingDTO.getUpcomingInfo().getAssigneeJson().equals("[]")) {
                execution.setStr("EXECUTION_ASSIGNEE_JSON", upcomingDTO.getUpcomingInfo().getAssigneeJson());
            }
            executionId = execution.getPkValue();
            metaService.update(execution);
        }
        insertTask(taskId, upcomingUserId, executionId, upcomingDTO, execution, submitType);
    }

    @Override
    public void completeUpcoming(UpcomingCommentInfoDTO upcomingCommentInfoDTO) {
        String taskId = upcomingCommentInfoDTO.getTaskId();
        String beanId = upcomingCommentInfoDTO.getBeanId();
        String comment = upcomingCommentInfoDTO.getComment();
        SubmitTypeEnum submitType = upcomingCommentInfoDTO.getSubmitType();
        Map<String, String> params = upcomingCommentInfoDTO.getParams();
        String piid = upcomingCommentInfoDTO.getPiid();
        String nodeId = upcomingCommentInfoDTO.getNodeId();
        Map<String, Object> bean = upcomingCommentInfoDTO.getBean();
        abstractCompleteUpcomingService.invoke(submitType.toString(), taskId, beanId, comment, piid, nodeId, params, bean);
        if (submitType.toString().equals(SubmitTypeEnum.OUTGOING.toString())) {
            return;
        }
        pushService.pushRefresh(SecurityUserHolder.getCurrentAccountRealUserId());
    }

    public void pushRefresh(UpcomingCommentInfoDTO upcomingCommentInfoDTO) {
        String taskId = upcomingCommentInfoDTO.getTaskId();
        ConditionsWrapper conditionsWrapper = ConditionsWrapper.builder().eq("TASK_ACTIVITI_TASK_ID", taskId);
        List<DynaBean> dynaBeans = metaService.select("JE_WORKFLOW_RN_TASK",
                conditionsWrapper);
        List<String> userIds = new ArrayList<>();
        if (dynaBeans.size() > 0) {
            for (DynaBean dynaBean : dynaBeans) {
                userIds.add(dynaBean.getStr("ASSIGNEE_ID"));
            }
        }

        if (!userIds.isEmpty()) {
            for (String userId : userIds) {
                String associationId = workFlowUserService.getAssociationIdById(userId);
                pushService.pushRefresh(associationId);
            }
        }
    }

    @Override
    public void pushUrge(String beanId, String funcCode, UrgeLogEntity urgeLogEntity, List<String> list) {
        String associationId = workFlowUserService.getAssociationIdById(urgeLogEntity.getTo());
        String content = "";
        if (urgeLogEntity.getReminderType().equals("MAIN_SEND")) {
            content = String.format(DESC_FORMAT, urgeLogEntity.getFromName(), urgeLogEntity.getProcessName(), urgeLogEntity.getContent());
        } else {
            content = String.format(DESC_COPY_FORMAT, urgeLogEntity.getFromName(), urgeLogEntity.getToName()
                    , urgeLogEntity.getProcessName(), urgeLogEntity.getContent());
        }
        MessageDTO messageDTO = MessageDTO.build(urgeLogEntity.getReminderName(), urgeLogEntity.getProcessName(),
                "", beanId, funcCode);
        pushService.pushUrgeMsg(associationId, messageDTO, list, content, urgeLogEntity.getReminderName());
    }

    public DynaBean insertExecution(String beanId, String executionId, Map<String, Object> bean,
                                    ProcessRemindTemplate processRemindTemplate, String funcCode, String tableCode,
                                    String modelName, SubmitTypeEnum submitType, String comment, String processKey,
                                    UpcomingDTO upcomingDTO) {
        List<DynaBean> list = metaService.select("JE_WORKFLOW_RN_EXECUTION", ConditionsWrapper.builder().eq("BUSINESS_KEY", beanId));
        DynaBean execution = new DynaBean("JE_WORKFLOW_RN_EXECUTION", true);
        if (list.size() > 0) {
            execution = list.get(0);
            commonService.buildModelCreateInfo(execution);
            execution.setStr("SY_CREATEUSERID", Authentication.getAuthenticatedUser().getDeptId());
            execution.setStr("EXECUTION_SUBMIT_TYPE", submitType.toString());
            execution.setStr("EXECUTION_LAST_COMMENT", comment);
            execution.setStr("EXECUTION_NODE_NAME", upcomingDTO.getNodeName());
            execution.setStr("EXECUTION_NODE_ID", upcomingDTO.getNodeId());
            if (upcomingDTO.getUpcomingInfo().getAssigneeJson() != null) {
                execution.setStr("EXECUTION_ASSIGNEE_JSON", upcomingDTO.getUpcomingInfo().getAssigneeJson());
            }
            execution.setStr("EXECUTION_SUBMIT_USER_NAME", Authentication.getAuthenticatedUser().getName());
            execution.setStr("EXECUTION_SUBMIET_USER_ID", Authentication.getAuthenticatedUser().getDeptId());
            execution.setStr("EXECUTION_SUBMIT_TIME", DateUtils.formatDateTime(new Date()));
            execution.setStr("JE_WORKFLOW_RN_EXECUTION_ID", executionId);
            execution.setStr("EXECUTION_CONTENT", buildContent(processRemindTemplate, modelName, submitType, upcomingDTO));
        } else {
            commonService.buildModelCreateInfo(execution);
            execution.setStr("EXECUTION_START_TIME", DateUtils.formatDateTime(new Date()));
            execution.setStr("SY_CREATEUSERID", Authentication.getAuthenticatedUser().getDeptId());
            execution.setStr("BUSINESS_KEY", beanId);
            execution.setStr("EXECUTION_CONTENT", buildContent(processRemindTemplate, modelName, submitType, upcomingDTO));
            execution.setStr("EXECUTION_TITLE", buildTitle(bean, processRemindTemplate, modelName, submitType, upcomingDTO));
            execution.setStr("EXECUTION_STARTER", upcomingDTO.getStartUserId());
            execution.setStr("EXECUTION_STARTER_NAME", workFlowUserService.getUserNameById(upcomingDTO.getStartUserId()));
            execution.setStr("EXECUTION_SUBMIT_TYPE", submitType.toString());
            execution.setStr("EXECUTION_LAST_COMMENT", comment);
            execution.setStr("EXECUTION_PROCESS_KEY", processKey);
            DynaBean dynaBean = metaService.selectOne("JE_WORKFLOW_PROCESSINFO", ConditionsWrapper.builder().eq("PROCESSINFO_KEY", processKey));
            execution.setStr("EXECUTION_PRODUCT_CODE", dynaBean.getStr("SY_PRODUCT_CODE"));
            execution.setStr("EXECUTION_PIID", upcomingDTO.getPiid());
            execution.setStr("EXECUTION_PDID", upcomingDTO.getPdid());
            execution.setStr("EXECUTION_NODE_NAME", upcomingDTO.getNodeName());
            execution.setStr("EXECUTION_NODE_ID", upcomingDTO.getNodeId());
            if (upcomingDTO.getUpcomingInfo().getAssigneeJson() != null) {
                execution.setStr("EXECUTION_ASSIGNEE_JSON", upcomingDTO.getUpcomingInfo().getAssigneeJson());
            }
            execution.setStr("EXECUTION_SUBMIT_USER_NAME", Authentication.getAuthenticatedUser().getName());
            execution.setStr("EXECUTION_SUBMIET_USER_ID", Authentication.getAuthenticatedUser().getDeptId());
            execution.setStr("EXECUTION_SUBMIT_TIME", DateUtils.formatDateTime(new Date()));
            execution.setStr("JE_WORKFLOW_RN_EXECUTION_ID", executionId);
            execution.setStr("TABLE_CODE", tableCode);
            execution.setStr("FUNC_CODE", funcCode);
            execution.setStr("FUNC_NAME", upcomingDTO.getFuncName());
            execution.setStr("PROCESS_NAME", upcomingDTO.getModelName());

        }
        metaService.insert(execution);
        return execution;
    }

    private String buildContent(ProcessRemindTemplate processRemindTemplate, String modelName, SubmitTypeEnum submitType, UpcomingDTO upcomingDTO) {
        String temple = processRemindTemplate.getTemplate();
        StringBuffer content = new StringBuffer();
        if (!Strings.isNullOrEmpty(temple)) {
            String variable = WorkFlowVariable.formatVariable(temple, MessageDTO.build(upcomingDTO).getVariables());
            String formatVariable = CommonSystemVariable.formatVariable(variable);
            content.append(formatVariable);
        }
        return content.toString();

    }

    public String buildTitle(Map<String, Object> bean, ProcessRemindTemplate processRemindTemplate,
                             String modelName, SubmitTypeEnum submitType, UpcomingDTO upcomingDTO) {
        StringBuffer title = new StringBuffer();
        title.append(Authentication.getAuthenticatedUser().getName());
        title.append("%s");
        title.append(submitType.getName());
        title.append("【" + modelName + "】");
        return title.toString();
    }

    private void insertTask(String taskId, String upcomingUserId, String executionId, UpcomingDTO upcomingDTO, DynaBean execution, SubmitTypeEnum submitType) {
        DynaBean task = new DynaBean("JE_WORKFLOW_RN_TASK", true);
        commonService.buildModelCreateInfo(task);
        task.setStr("SY_CREATEUSERID", Authentication.getAuthenticatedUser().getDeptId());
        task.setStr("TASK_ACTIVITI_TASK_ID", taskId);
        task.setStr("TASK_HANDLE", "0");
        task.setStr("TASK_COLLECT_TIME", "");
        task.setStr("TASK_COLLECT", "0");
        task.setStr("TASK_DELAY", "0");
        task.setStr("ASSIGNEE_ID", upcomingUserId);
        task.setStr("ASSIGNEE_NAME", workFlowUserService.getUserNameById(upcomingUserId));
        task.setStr("JE_WORKFLOW_RN_EXECUTION_ID", executionId);
        task.setStr("TASK_COMMENT_NAME", "");
        task.setStr("TASK_COMMENT_TYPE", "");
        task.setStr("TASK_SUBMIT_USER_ID", "");
        task.setStr("TASK_SUBMIT_USER_NAME", "");
        task.setStr("TASK_SUBMIT_TYPE_NAME", "");
        task.setStr("TASK_SUBMIT_TYPE_NAME", "");
        task.setStr("TASK_NODE_ID", upcomingDTO.getNodeId());
        task.setStr("TASK_NODE_NAME", upcomingDTO.getNodeName());
        task.setStr("TASK_PDID", upcomingDTO.getPdid());
        task.setStr("TASK_PIID", upcomingDTO.getPiid());
        metaService.insert(task);
        //将这已办的任务删除，后续查询待办比较快

        List<DynaBean> list = metaService.select("JE_WORKFLOW_RN_TASK", ConditionsWrapper.builder()
                .eq("ASSIGNEE_ID", upcomingUserId).eq("TASK_PIID", upcomingDTO.getPiid())
                .eq("TASK_HANDLE", "1"));
        for (DynaBean dynaBean : list) {
            metaService.delete("JE_WORKFLOW_RN_TASK",
                    ConditionsWrapper.builder().eq("JE_WORKFLOW_RN_TASK_ID", dynaBean.getPkValue()));
        }
        String associationId = workFlowUserService.getAssociationIdById(upcomingUserId);
        //发起不会申请人发送消息推送
        if (upcomingDTO.getIsSponsor()) {
            return;
        }
        if (!Arrays.asList(NOT_PUSH_MESSAGE_TYPES).contains(submitType.toString())) {
            RepositoryService repositoryService = SpringContextHolder.getBean(RepositoryService.class);
            BpmnModel bpmnModel = repositoryService.getBpmnModel(upcomingDTO.getPdid());
            //推送类型
            List<ProcessRemindTypeEnum> processRemindTypeEnumList = bpmnModel.getMainProcess().getMessageSetting().getMessages();
            //推送的模板
            List<ProcessRemindTemplate> messageDefinitions = bpmnModel.getMainProcess().getMessageSetting().getMessageDefinitions();

            if (bpmnModel != null) {
                FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(upcomingDTO.getNodeId());

                if (flowElement instanceof KaiteBaseUserTask) {
                    KaiteBaseUserTask kaiteBaseUserTask = (KaiteBaseUserTask) flowElement;
                    if (!kaiteBaseUserTask.getTaskBasicConfig().getRemind()) {
                        pushService.pushDiffTypeMsg(associationId, processRemindTypeEnumList, messageDefinitions, MessageDTO.build(upcomingDTO));
                    }
                }
            } else {
                pushService.pushDiffTypeMsg(associationId, processRemindTypeEnumList, messageDefinitions, MessageDTO.build(upcomingDTO));
            }
            pushService.pushRefresh(associationId);
        }
    }
}
